#include "stream.h"
#include "utility.h"

#include <cassert>
#include <cstring>
#include <iostream>


namespace stdf {

Stream::Stream() {
    init();
}

Stream::Stream( string name, RWMode mode) {
    init();
    streamName = name;
    rwMode     = mode;
}

void Stream::init(void) {
    desiredByteOrder = nativeByteOrder = getNativeByteOrder();
    swap = false;
    byteCount = 0;
    recStartCount = 0;
    recReadLengthMax = 0;
    recReadCount = 0;

    recordTypeCount[FAR] = 0;
    recordTypeCount[ATR] = 0;
    recordTypeCount[MIR] = 0;
    recordTypeCount[MRR] = 0;
    recordTypeCount[PCR] = 0;
    recordTypeCount[HBR] = 0;
    recordTypeCount[SBR] = 0;
    recordTypeCount[PMR] = 0;
    recordTypeCount[PGR] = 0;
    recordTypeCount[PLR] = 0;
    recordTypeCount[RDR] = 0;
    recordTypeCount[SDR] = 0;
    recordTypeCount[WIR] = 0;
    recordTypeCount[WRR] = 0;
    recordTypeCount[WCR] = 0;
    recordTypeCount[PIR] = 0;
    recordTypeCount[PRR] = 0;
    recordTypeCount[TSR] = 0;
    recordTypeCount[PTR] = 0;
    recordTypeCount[MPR] = 0;
    recordTypeCount[FTR] = 0;
    recordTypeCount[BPS] = 0;
    recordTypeCount[EPS] = 0;
    recordTypeCount[GDR] = 0;
    recordTypeCount[DTR] = 0;
    recordTypeCount[Header] = 0;
    recordTypeCount[Reserved] = 0;
}

Stream::~Stream() {
}

void Stream::setByteOrder( ByteOrder order) { 
    desiredByteOrder = order;
    if(desiredByteOrder != nativeByteOrder)
        swap = true; 
}

bool Stream::open(string name, RWMode mode) {
    bool success = false;

    streamName = name;
    rwMode     = mode;

    if(mode == ReadOnly) {
        // RWMode is ReadOnly, open file for reading and prohibit writing from
        // now on
        filestream.open( streamName.c_str(), ios::in | ios::binary);
        if(! filestream.is_open()) {
            cerr << "ERROR: Could not open STDF file " << streamName << " for read." << endl;
            rwMode = None;
            streamName = "";
            success = false;
        }
        else
            success = true;
    }
    else if(mode == WriteOnly) {
        // RWMode is WriteOnly, open file for writing and prohibit reading from
        // now on
        // TODO: For now truncate file to zero if it already exists, later on
        //       Add code to append STDF data to file and update audit trails record.

        // Check if filestream is already open , if so close and open again with new filename
        if(filestream.is_open())
            filestream.close();

        filestream.open( streamName.c_str(), ios::out | ios::trunc | ios::binary);
        if(! filestream.is_open()) {
            cerr << "ERROR: Could not open STDF file " << streamName << " for write." << endl;
#if PLATFORM == Linux
//            cerr << "   In -> " <<  __ASSERT_FUNCTION << endl;
#endif
            rwMode = None;
            streamName = "";
            success = false;
        }
        else
            success = true;
    }
    else {
        // RWMode is None , wait till first IO operation to set it correctly
    }

    return success;
}

bool Stream::close() {
    bool success = false;

    if(filestream.is_open())
        filestream.close();

    success = true;

    return success;
}

int Stream::read(U1& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good())
        return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    filestream.read( (char *)&val, 1);

    byteCount++;
    recReadCount++;

    return 1;
}

int Stream::read(C1& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    const unsigned int size = 1;
    filestream.read((char *) &val, size);

    byteCount += size;
    recReadCount += size;

    return size;
}

int Stream::read(U2& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    if(!swap) {
        filestream.read( (char *)&val, 2);
    }
    else {
        char buf[2];
        filestream.read( buf, 2);
        unsigned char *p = (unsigned char *)&val;
        *p++ = buf[1];
        *p   = buf[0];
    }
    byteCount += 2;
    recReadCount += 2;

    return 2;
}

int Stream::read(U4& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    if(!swap) {
        filestream.read( (char *)&val, 4);
    }
    else {
        char buf[4];
        filestream.read( buf, 4);
        unsigned char *p = (unsigned char *)&val;
        *p++ = buf[3];
        *p++ = buf[2];
        *p++ = buf[1];
        *p   = buf[0];
    }
    byteCount += 4;
    recReadCount += 4;

    return 4;
}

int Stream::read(I2& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    const unsigned int size = 2;
    char buf[size];
    filestream.read( buf, size);
    char *p = (char *)&val;
    translate( buf, p, size, swap);

    byteCount += size;
    recReadCount += size;

    return size;
}

int Stream::read(I4& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    const unsigned int size = 4;
    char buf[size];
    filestream.read( buf, size);
    char *p = (char *)&val;
    translate( buf, p, size, swap);

    byteCount += size;
    recReadCount += size;

    return size;
}

int Stream::read(R4& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    const unsigned int size = 4;
    char buf[size];
    filestream.read( buf, size);
    char *p = (char *)&val;
    translate( buf, p, size, swap);

    byteCount += size;
    recReadCount += size;

    return size;
}

int Stream::read(R8& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    const unsigned int size = 8;
    char buf[size];
    filestream.read( buf, size);
    char *p = (char *)&val;
    translate( buf, p, size, swap);

    byteCount += size;
    recReadCount += size;
    return size;
}

int Stream::read(CN& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    U1 len = filestream.get();
    string temp = "";

    if(len > 0) {
        char buf[len + 1];
        filestream.read(buf, len);
        temp.assign( buf, len);
    }
    val = temp;

    byteCount += (len + 1);
    recReadCount += (len + 1);

    return (len + 1);
}

int Stream::read(BN& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    U1 len = 0;
    *this >> len;

    vector<U1> data;

    if(len > 0) {
        char buf[len];
        filestream.read( buf, len);
        for(unsigned int i=0; i < len; i++)
            data.push_back(buf[i]);
    }
    val = data;

    byteCount += (len + 1);
    recReadCount += (len + 1);

    return (len + 1);
}

int Stream::read(DN& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    U2 numBits = 0;
    *this >> numBits;
    U2 numBytes = (numBits / 8) + 1;
    if((numBits % 8) == 0) numBytes--;

    vector<U1> data;

    if(numBits > 0) {
        char buf[numBytes];
        filestream.read( buf, numBytes);
        for(unsigned int i=0; i < numBytes; i++)
            data.push_back(buf[i]);
    }
    val.set( numBits, data);

    byteCount += (numBytes + 2);
    recReadCount += (numBytes + 2);

    return (numBytes + 2);
}

int Stream::read(VN& val) {
    assert(rwMode == ReadOnly);
    if (!filestream.good()) return 0;
    if (recReadCount >= recReadLengthMax)
        return 0;

    *this >> val.code;

    switch(val.code) {
        case 0: break;
        case 1:  *this >> val.u1; break;
        case 2:  *this >> val.u2; break;
        case 3:  *this >> val.u4; break;
        case 4:  *this >> val.i1; break;
        case 5:  *this >> val.i2; break;
        case 6:  *this >> val.i4; break;
        case 7:  *this >> val.r4; break;
        case 8:  *this >> val.r8; break;
        case 10: *this >> val.cn; break;
        case 11: *this >> val.bn; break;
        case 12: *this >> val.dn; break;
        case 13: *this >> val.n1; break;
        default: break;
    };

    int readBytes = val.getLength();

    byteCount += readBytes;
    recReadCount += readBytes;

    return readBytes;
}

int Stream::write(U1& val) {
    assert( rwMode = WriteOnly);
    if (!filestream.good()) return 0;

    filestream.put(val);
    byteCount++;

    return 1;
}

int Stream::write(U2& val) {
    assert( rwMode = WriteOnly);
    if (!filestream.good()) return 0;

    const unsigned int numBytes = 2;
    filestream.write( (char *) &val, numBytes);
    byteCount += numBytes;

    return numBytes;
}

int Stream::write(U4& val) {
    assert( rwMode = WriteOnly);
    if (!filestream.good()) return 0;

    const unsigned int numBytes = 4;
    filestream.write( (char *) &val, numBytes);
    byteCount += numBytes;

    return numBytes;
}

int Stream::write(I2& val) {
    assert( rwMode = WriteOnly);
    if (!filestream.good()) return 0;

    const unsigned int numBytes = 2;
    filestream.write( (char *) &val, numBytes);
    byteCount += numBytes;

    return numBytes;
}

int Stream::write(I4& val) {
    assert( rwMode = WriteOnly);
    if (!filestream.good()) return 0;

    const unsigned int numBytes = 4;
    filestream.write( (char *) &val, numBytes);
    byteCount += numBytes;

    return numBytes;
}

int Stream::write(R4& val) {
    assert( rwMode = WriteOnly);
    if (!filestream.good()) return 0;

    const unsigned int numBytes = 4;
    filestream.write( (char *) &val, numBytes);
    byteCount += numBytes;

    return numBytes;
}

int Stream::write(R8& val) {
    assert( rwMode = WriteOnly);
    if (!filestream.good()) return 0;

    const unsigned int numBytes = 8;
    filestream.write( (char *) &val, numBytes);
    byteCount += numBytes;

    return numBytes;
}

int Stream::write(C1& val) {
    assert(rwMode == WriteOnly);
    if (!filestream.good()) return 0;

    const unsigned int size = 1;
    filestream.write( (char *) &val, size);
    byteCount += size;

    return size;
}

int Stream::write(CN& val) {
    assert(rwMode == WriteOnly);
    if (!filestream.good()) return 0;

    const unsigned char numBytes = val.getLength() - 1;
    filestream.put(numBytes);
    filestream.write( val.str().data(), numBytes);
    byteCount += (numBytes + 1);

    return (numBytes + 1);
}

int Stream::write(BN& val) {
    assert(rwMode == WriteOnly);
    if (!filestream.good()) return 0;

    const unsigned char numBytes = val.getLength() - 1;
    filestream.put(numBytes);

    for(unsigned int i=0; i < numBytes; i++)
        filestream.put( val.data[i]);

    byteCount += (numBytes + 1);

    return (numBytes + 1);
}

int Stream::write(DN& val) {
    assert(rwMode == WriteOnly);
    if (!filestream.good()) return 0;

    *this << val.count;

    U2 numBytes = val.getNumBytes();
    if(val.count > 0) {
        for(unsigned int i=0; i < numBytes; i++)
            filestream.put( val.data[i]);
    }

    byteCount += (numBytes + 2);

    return (numBytes + 2);
}

int Stream::write(VN& val) {
    assert(rwMode == WriteOnly);
    if (!filestream.good()) return 0;

    switch(val.code) {
        case  0 : break;
        case  1 : write(val.u1); break;
        case  2 : write(val.u2); break;
        case  3 : write(val.u4); break;
        default : break;
    };

    unsigned int numBytes = val.getLength();

    byteCount += numBytes;

    return numBytes;
}


int Stream::skip( U2 length) {
    assert(rwMode == ReadOnly);

    if(length > 0) {
        char buf[length];
        filestream.read( buf, length);
    }

    return ((int) length);
}


int Stream::readRecordHeader(RecordHeader& rh) {
    beginRecord(4);

    read(rh.length);
    read(rh.type);
    read(rh.subType);

    currRecordHeader = rh;

    return endRecord();
}

void Stream::beginRecord(const unsigned int len) {
    recStartCount = byteCount;

    if(len == 0)
       recReadLengthMax = currRecordHeader.length;
   else
       recReadLengthMax = len;
   recReadCount = 0;
}

int Stream::endRecord(void) {
    return ((int) (byteCount - recStartCount));
    //return recReadCount;
}

int Stream::readRecord(FARRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.cpuType);
    read(rec.stdfVersion);

    recordTypeCount[FAR] = recordTypeCount[FAR] + 1;

    return endRecord();
}

int Stream::readRecord(ATRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.modificationTime);
    read(rec.commandLine);
    recordTypeCount[ATR] = recordTypeCount[ATR] + 1;

    return endRecord();
}

int Stream::readRecord(MIRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.setupTime);
    read(rec.startTime);
    read(rec.stationNumber);
    read(rec.modeCode);
    read(rec.retestCode);
    read(rec.protectionCode);
    read(rec.burninTime);
    read(rec.commandModeCode);
    read(rec.lotID);
    read(rec.partType);
    read(rec.nodeName);
    read(rec.testerType);
    read(rec.jobName);
    read(rec.jobRevision);
    read(rec.sublotID);
    read(rec.operatorName);
    read(rec.executiveType);
    read(rec.executiveVersion);
    read(rec.testCode);
    read(rec.testTemp);
    read(rec.userText);
    read(rec.auxFile);
    read(rec.packageType);
    read(rec.familyID);
    read(rec.dateCode);
    read(rec.facilityID);
    read(rec.floorID);
    read(rec.processID);
    read(rec.operatorFrequency);
    read(rec.specName);
    read(rec.specVersion);
    read(rec.flowID);
    read(rec.setupID);
    read(rec.designRevision);
    read(rec.engineeringLotID);
    read(rec.romCode);
    read(rec.serialNumber);
    read(rec.supervisorName);
    recordTypeCount[MIR] = recordTypeCount[MIR] + 1;

    return endRecord();
}

int Stream::readRecord(SDRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.headNumber);
    read(rec.siteGroup);
    read(rec.siteCount);
    for(unsigned int i=0; i < rec.siteCount; i++) {
        U1 siteNum = 0;
        read(siteNum);
        rec.siteNumbers.push_back(siteNum);
    }
    read(rec.handlerType);
    read(rec.handlerID);
    read(rec.probeCardType);
    read(rec.probeCardID);
    read(rec.loadBoardType);
    read(rec.loadBoardID);
    read(rec.dibType);
    read(rec.dibID);
    read(rec.cableType);
    read(rec.cableID);
    read(rec.contactorType);
    read(rec.contactorID);
    read(rec.laserType);
    read(rec.laserID);
    read(rec.extraType);
    read(rec.extraID);
    recordTypeCount[SDR] = recordTypeCount[SDR] + 1;

    return endRecord();
}

int Stream::readRecord(PMRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.pmrIndex);
    read(rec.channelType);
    read(rec.channelName);
    read(rec.physicalName);
    read(rec.logicalName);
    read(rec.headNumber);
    read(rec.siteNumber);
    recordTypeCount[PMR] = recordTypeCount[PMR] + 1;

    return endRecord();
}

int Stream::readRecord(PGRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.groupIndex);
    read(rec.groupName);
    read(rec.indexCount);
    for(unsigned int i=0; i < rec.indexCount; i++) {
        U2 index = 0;
        read(index);
        rec.pmrIndex.push_back(index);
    }
    recordTypeCount[PGR] = recordTypeCount[PGR] + 1;

    return endRecord();
}

int Stream::readRecord(PIRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.headNumber);
    read(rec.siteNumber);
    recordTypeCount[PIR] = recordTypeCount[PIR] + 1;

    return endRecord();
}

int Stream::readRecord(PLRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.groupCount);
    for(unsigned int i=0; i < rec.groupCount; i++) {
        U2 index = 0;
        read(index);
        rec.groupIndex.push_back(index);
    }
    for(unsigned int i=0; i < rec.groupCount; i++) {
        U2 mode = 0;
        read(mode);
        rec.groupMode.push_back(mode);
    }
    for(unsigned int i=0; i < rec.groupCount; i++) {
        U1 radix = 0;
        read(radix);
        rec.groupRadix.push_back(radix);
    }
    for(unsigned int i=0; i < rec.groupCount; i++) {
        CN charRight;
        read(charRight);
        rec.programCharRight.push_back(charRight);
    }
    for(unsigned int i=0; i < rec.groupCount; i++) {
        CN charRight;
        read(charRight);
        rec.returnCharRight.push_back(charRight);
    }
    for(unsigned int i=0; i < rec.groupCount; i++) {
        CN charLeft;
        read(charLeft);
        rec.programCharLeft.push_back(charLeft);
    }
    for(unsigned int i=0; i < rec.groupCount; i++) {
        CN charLeft;
        read(charLeft);
        rec.returnCharLeft.push_back(charLeft);
    }
    recordTypeCount[PLR] = recordTypeCount[PLR] + 1;

    return endRecord();
}

int Stream::readRecord(PTRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.testNumber);
    read(rec.headNumber);
    read(rec.siteNumber);
    read(rec.testFlag);
    read(rec.parmFlag);
    read(rec.testResult);
    read(rec.testText);
    read(rec.alarmID);
    read(rec.optFlag);
    if(rec.optFlag != 0xFF) {
        read(rec.resultScale);
        read(rec.llmScale);
        read(rec.hlmScale);
        read(rec.loLimit);
        read(rec.hiLimit);
        read(rec.units);
        read(rec.cResFmt);
        read(rec.cLlmFmt);
        read(rec.cHlmFmt);
        read(rec.loSpec);
        read(rec.hiSpec);
    }
    recordTypeCount[PTR] = recordTypeCount[PTR] + 1;

    return endRecord();
}

int Stream::readRecord(PRRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.headNumber);
    read(rec.siteNumber);
    read(rec.partFlag);
    read(rec.numTests);
    read(rec.hardBin);
    read(rec.softBin);
    read(rec.xCoord);
    read(rec.yCoord);
    read(rec.testTime);
    read(rec.partID);
    read(rec.partText);
    read(rec.partFix);
    recordTypeCount[PRR] = recordTypeCount[PRR] + 1;

    return endRecord();
}

int Stream::readRecord(TSRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.headNumber);
    read(rec.siteNumber);
    read(rec.testType);
    read(rec.testNumber);
    read(rec.execCount);
    read(rec.failCount);
    read(rec.alarmCount);
    read(rec.testName);
    read(rec.sequencerName);
    read(rec.testLabel);
    read(rec.optFlag);
    read(rec.testTime);
    read(rec.testMin);
    read(rec.testMax);
    read(rec.testSums);
    read(rec.testSquares);
    recordTypeCount[TSR] = recordTypeCount[TSR] + 1;

    return endRecord();
}

int Stream::readRecord(WCRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.waferSize);
    read(rec.dieHeight);
    read(rec.dieWidth);
    read(rec.waferUnits);
    read(rec.waferFlat);
    read(rec.centerX);
    read(rec.centerY);
    read(rec.positiveX);
    read(rec.positiveY);
    recordTypeCount[WCR] = recordTypeCount[WCR] + 1;

    return endRecord();
}

int Stream::readRecord(WIRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.headNumber);
    read(rec.siteGroup);
    read(rec.startTime);
    read(rec.waferID);
    recordTypeCount[WIR] = recordTypeCount[WIR] + 1;

    return endRecord();
}

int Stream::readRecord(WRRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.headNumber);
    read(rec.siteGroup);
    read(rec.finishTime);
    read(rec.partCount);
    read(rec.retestCount);
    read(rec.abortCount);
    read(rec.goodCount);
    read(rec.funcCount);
    read(rec.waferID);
    read(rec.fabWaferID);
    read(rec.waferFrameID);
    read(rec.waferMaskID);
    read(rec.userDescription);
    read(rec.execDescription);
    recordTypeCount[WRR] = recordTypeCount[WRR] + 1;

    return endRecord();
}

int Stream::readRecord(HBRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.headNumber);
    read(rec.siteNumber);
    read(rec.hbinNumber);
    read(rec.hbinCount);
    read(rec.hbinPF);
    read(rec.hbinName);
    recordTypeCount[HBR] = recordTypeCount[HBR] + 1;

    return endRecord();
}

int Stream::readRecord(SBRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.headNumber);
    read(rec.siteNumber);
    read(rec.sbinNumber);
    read(rec.sbinCount);
    read(rec.sbinPF);
    read(rec.sbinName);
    recordTypeCount[SBR] = recordTypeCount[SBR] + 1;

    return endRecord();
}

int Stream::readRecord(PCRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.headNumber);
    read(rec.siteNumber);
    read(rec.partCount);
    read(rec.retestCount);
    read(rec.abortCount);
    read(rec.goodCount);
    read(rec.funcCount);
    recordTypeCount[PCR] = recordTypeCount[PCR] + 1;

    return endRecord();
}

int Stream::readRecord(MRRRecord& rec, const unsigned int len) {
    beginRecord(len);

    read(rec.finishTime);
    read(rec.dispositionCode);
    read(rec.userLotDescription);
    read(rec.executiveLotDescription);

    recordTypeCount[MRR] = recordTypeCount[MRR] + 1;

    return endRecord();
}

Stream& Stream::operator<<(U1& u1) {
    assert( rwMode = WriteOnly);

    filestream.put(u1);
    byteCount++;

    return *this;
}

Stream& Stream::operator>>(U1& u1) {
    assert(rwMode == ReadOnly);

    //u1 = filestream.get();
    filestream.read( (char *)&u1, 1);

    byteCount++;

    return *this;
}

Stream& Stream::operator<<(U2& u2) {
    assert( rwMode = WriteOnly);

    filestream.write( (char *) &u2, 2);
    byteCount += 2;

    return *this;
}

Stream& Stream::operator>>(U2& u2) {
    assert(rwMode == ReadOnly);

    if(!swap) {
        filestream.read( (char *)&u2, 2);
    }
    else {
        char buf[2];
        filestream.read( buf, 2);
        unsigned char *p = (unsigned char *)&u2;
        *p++ = buf[1];
        *p   = buf[0];
    }
    byteCount += 2;

    return *this;
}

Stream& Stream::operator<<(U4& u4) {
    assert( rwMode = WriteOnly);

    filestream.write( (char *) &u4, 4);
    byteCount += 4;

    return *this;
}

Stream& Stream::operator>>(U4& u4) {
    assert(rwMode == ReadOnly);

    if(!swap) {
        filestream.read( (char *)&u4, 4);
    }
    else {
        char buf[4];
        filestream.read( buf, 4);
        unsigned char *p = (unsigned char *)&u4;
        *p++ = buf[3];
        *p++ = buf[2];
        *p++ = buf[1];
        *p   = buf[0];
    }
    byteCount += 4;

    return *this;
}

Stream& Stream::operator<<(I2& i2) {
    assert( rwMode = WriteOnly);

    filestream.write( (char *) &i2, 2);
    byteCount += 2;

    return *this;
}

Stream& Stream::operator>>(I2& i2) {
    assert(rwMode == ReadOnly);

    char buf[2];
    filestream.read( buf, 2);
    char *p = (char *)&i2;
    translate( buf, p, 2, swap);

    byteCount += 2;

    return *this;
}

Stream& Stream::operator<<(I4& i4) {
    assert( rwMode = WriteOnly);

    filestream.write( (char *) &i4, 4);
    byteCount += 4;

    return *this;
}

Stream& Stream::operator>>(I4& i4) {
    assert(rwMode == ReadOnly);

    char buf[4];
    filestream.read( buf, 4);
    char *p = (char *)&i4;
    translate( buf, p, 4, swap);

    byteCount += 4;

    return *this;
}

Stream& Stream::operator<<(R4& val) {
    assert( rwMode = WriteOnly);

    const unsigned int size = 4;
    filestream.write( (char *) &val, size);
    byteCount += size;

    return *this;
}

Stream& Stream::operator>>(R4& val) {
    assert(rwMode == ReadOnly);

    const unsigned int size = 4;
    char buf[size];
    filestream.read( buf, size);
    char *p = (char *)&val;
    translate( buf, p, size, swap);

    byteCount += size;

    return *this;
}

Stream& Stream::operator<<(R8& val) {
    assert( rwMode = WriteOnly);

    const unsigned int size = 8;
    filestream.write( (char *) &val, size);
    byteCount += size;

    return *this;
}

Stream& Stream::operator>>(R8& val) {
    assert(rwMode == ReadOnly);

    const unsigned int size = 8;
    char buf[size];
    filestream.read( buf, size);
    char *p = (char *)&val;
    translate( buf, p, size, swap);

    byteCount += size;

    return *this;
}

Stream& Stream::operator<<(C1& val) {
    assert(rwMode == WriteOnly);

    const unsigned int size = 1;
    filestream.write( (char *) &val, size);
    byteCount += size;

    return *this;
}

Stream& Stream::operator>>(C1& val) {
    assert(rwMode == ReadOnly);

    const unsigned int size = 1;
    filestream.read((char *) &val, size);

    byteCount += size;

    return *this;
}

Stream& Stream::operator<<(CN& val) {
    assert(rwMode == WriteOnly);

    const unsigned char numBytes = val.getLength() - 1;
    filestream.put(numBytes);
    filestream.write( val.str().data(), numBytes);
    byteCount += (numBytes + 1);

    return *this;
}

Stream& Stream::operator>>(CN& val) {
    assert(rwMode == ReadOnly);

    U1 len = filestream.get();
    string temp = "";

    if(len > 0) {
        char buf[len + 1];
        filestream.read(buf, len);
        temp.assign( buf, len);
    }
    val = temp;

    byteCount += (len + 1);

    return *this;
}

Stream& Stream::operator>>(BN& val) {
    assert(rwMode == ReadOnly);

    U1 len = 0;
    *this >> len;

    vector<U1> data;

    if(len > 0) {
        char buf[len];
        filestream.read( buf, len);
        for(unsigned int i=0; i < len; i++)
            data.push_back(buf[i]);
    }
    val = data;

    return *this;
}

Stream& Stream::operator<<(BN& val) {
    assert(rwMode == WriteOnly);

    const unsigned char numBytes = val.getLength() - 1;
    filestream.put(numBytes);

    for(unsigned int i=0; i < numBytes; i++)
        filestream.put( val.data[i]);

    byteCount += (numBytes + 1);

    return *this;
}

Stream& Stream::operator>>(DN& val) {
    assert(rwMode == ReadOnly);

    U2 numBits = 0;
    *this >> numBits;
    U2 numBytes = (numBits / 8) + 1;
    if((numBits % 8) == 0) numBytes--;

    vector<U1> data;

    if(numBits > 0) {
        char buf[numBytes];
        filestream.read( buf, numBytes);
        for(unsigned int i=0; i < numBytes; i++)
            data.push_back(buf[i]);
    }
    val.set( numBits, data);

    byteCount += (numBytes + 2);

    return *this;
}

Stream& Stream::operator<<(DN& val) {
    assert(rwMode == WriteOnly);

    *this << val.count;

    U2 numBytes = val.getNumBytes();
    if(val.count > 0) {
        for(unsigned int i=0; i < numBytes; i++)
            filestream.put( val.data[i]);
    }

    byteCount += (numBytes + 2);

    return *this;
}

Stream& Stream::operator>>(VN& val) {
    assert(rwMode == ReadOnly);

    *this >> val.code;

    switch(val.code) {
        case 0: break;
        case 1:  *this >> val.u1; break;
        case 2:  *this >> val.u2; break;
        case 3:  *this >> val.u4; break;
        case 4:  *this >> val.i1; break;
        case 5:  *this >> val.i2; break;
        case 6:  *this >> val.i4; break;
        case 7:  *this >> val.r4; break;
        case 8:  *this >> val.r8; break;
        case 10: *this >> val.cn; break;
        case 11: *this >> val.bn; break;
        case 12: *this >> val.dn; break;
        case 13: *this >> val.n1; break;
        default: break;
    };

    byteCount += val.getLength();

    return *this;
}
Stream& Stream::operator<<(VN& val) {
    assert(rwMode == WriteOnly);

    switch(val.code) {
        case  0 : break;
        case  1 : write(val.u1); break;
        case  2 : write(val.u2); break;
        case  3 : write(val.u4); break;
        default : break;
    };

    unsigned int numBytes = val.getLength();

    byteCount += numBytes;

    return *this;
}

/////////////////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////

Stream& Stream::operator<<(RecordHeader& rh) {
    *this << rh.length << rh.type << rh.subType;
    return *this;
}

Stream& Stream::operator>>(RecordHeader& rh) {
    *this >> rh.length >> rh.type >> rh.subType;
    return *this;
}

Stream& Stream::operator<<(FARRecord& far) {
    *this << far.cpuType << far.stdfVersion;
    return *this;
}

Stream& Stream::operator>>(FARRecord& far) {
    *this >> far.cpuType >> far.stdfVersion;
    return *this;
}

Stream& Stream::operator<<(ATRRecord& atr) {
    *this << atr.modificationTime << atr.commandLine;
    return *this;
}

Stream& Stream::operator>>(ATRRecord& atr) {
    *this >> atr.modificationTime >> atr.commandLine;
    return *this;
}

Stream& Stream::operator<<(MIRRecord& mir) {
    *this MIR_ORDER(<<, mir);
    return *this;
}

Stream& Stream::operator>>(MIRRecord& mir) {
    *this MIR_ORDER(>>, mir);
    return *this;
}

} // end namespace
